From 4ba2b181bfdac308e5f10f707c5a493ad93d1679 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Tue, 16 Dec 2008 20:09:20 +0100 Subject: [PATCH] Implement shaped windows --- gdk/gdkinternals.h | 2 + gdk/gdkwindow.c | 98 +++++++++++++++++++++++++++++------- gdk/gdkwindow.h | 2 + gdk/x11/gdkwindow-x11.c | 108 ++++++++++++++++++++++++++++------------ 4 files changed, 159 insertions(+), 51 deletions(-) diff --git a/gdk/gdkinternals.h b/gdk/gdkinternals.h index 2766c8dfb7..7c119a493f 100644 --- a/gdk/gdkinternals.h +++ b/gdk/gdkinternals.h @@ -336,6 +336,8 @@ void _gdk_windowing_window_get_offsets (GdkWindow *window, gint *x_offset, gint *y_offset); GdkRegion *_gdk_windowing_window_get_shape (GdkWindow *window); +GdkRegion *_gdk_windowing_get_shape_for_mask (GdkBitmap *mask); + void _gdk_windowing_get_pointer (GdkDisplay *display, GdkScreen **screen, diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c index 9655e31c60..6ea81d042d 100644 --- a/gdk/gdkwindow.c +++ b/gdk/gdkwindow.c @@ -457,10 +457,25 @@ remove_child_area (GdkWindowObject *private, r.y = child->y; r.width = child->width; r.height = child->height; - + child_region = gdk_region_rectangle (&r); + + if (child->shape) + gdk_region_intersect (child_region, child->shape); + else if (private->window_type == GDK_WINDOW_FOREIGN) + { + GdkRegion *shape; + shape = _gdk_windowing_window_get_shape ((GdkWindow *)child); + if (shape) + { + gdk_region_intersect (child_region, shape); + gdk_region_destroy (shape); + } + } + gdk_region_subtract (region, child_region); gdk_region_destroy (child_region); + } } @@ -512,7 +527,7 @@ recompute_visible_regions_internal (GdkWindowObject *private, r.width = private->width; r.height = private->height; new_clip = gdk_region_rectangle (&r); - + if (private->parent != NULL && GDK_WINDOW_TYPE (private->parent) != GDK_WINDOW_ROOT) { gdk_region_intersect (new_clip, private->parent->clip_region); @@ -524,6 +539,9 @@ recompute_visible_regions_internal (GdkWindowObject *private, /* Convert from parent coords to window coords */ gdk_region_offset (new_clip, -private->x, -private->y); + if (private->shape) + gdk_region_intersect (new_clip, private->shape); + if (private->clip_region == NULL || !gdk_region_equal (private->clip_region, new_clip)) clip_region_changed = TRUE; @@ -562,9 +580,10 @@ recompute_visible_regions_internal (GdkWindowObject *private, gdk_window_has_impl (private) && /* Not for offscreens */ private->window_type != GDK_WINDOW_OFFSCREEN && - /* or for toplevels */ - private->parent != NULL && - GDK_WINDOW_TYPE (private->parent) != GDK_WINDOW_ROOT && + /* or for non-shaped toplevels */ + (private->shaped || + (private->parent != NULL && + GDK_WINDOW_TYPE (private->parent) != GDK_WINDOW_ROOT)) && /* or for foreign windows */ GDK_WINDOW_TYPE (private) != GDK_WINDOW_FOREIGN ) @@ -6171,12 +6190,19 @@ gdk_window_shape_combine_mask (GdkWindow *window, gint y) { GdkWindowObject *private; + GdkRegion *region; g_return_if_fail (GDK_IS_WINDOW (window)); private = (GdkWindowObject *) window; - GDK_WINDOW_IMPL_GET_IFACE (private->impl)->shape_combine_mask (window, mask, x, y); + region = _gdk_windowing_get_shape_for_mask (mask); + + gdk_window_shape_combine_region (window, + region, + x, y); + + gdk_region_destroy (region); } /** @@ -6214,7 +6240,47 @@ gdk_window_shape_combine_region (GdkWindow *window, private = (GdkWindowObject *) window; - GDK_WINDOW_IMPL_GET_IFACE (private->impl)->shape_combine_region (window, shape_region, offset_x, offset_y); + if (GDK_WINDOW_DESTROYED (window)) + return; + + private->shaped = (shape_region != NULL); + + if (private->shape) + gdk_region_destroy (private->shape); + + if (shape_region) + { + private->shape = gdk_region_copy (shape_region); + gdk_region_offset (private->shape, offset_x, offset_y); + } + else + private->shape = NULL; + + recompute_visible_regions (private, TRUE, FALSE); +} + +static void +do_child_shapes (GdkWindow *window, + gboolean merge) +{ + GdkWindowObject *private; + GdkRectangle r; + GdkRegion *region; + + private = (GdkWindowObject *) window; + + r.x = 0; + r.y = 0; + r.width = private->width; + r.height = private->height; + + region = gdk_region_rectangle (&r); + remove_child_area (private, NULL, region); + + if (merge && private->shape) + gdk_region_subtract (region, private->shape); + + gdk_window_shape_combine_region (window, region, 0, 0); } /** @@ -6229,13 +6295,9 @@ gdk_window_shape_combine_region (GdkWindow *window, void gdk_window_set_child_shapes (GdkWindow *window) { - GdkWindowObject *private; - g_return_if_fail (GDK_IS_WINDOW (window)); - private = (GdkWindowObject *) window; - - GDK_WINDOW_IMPL_GET_IFACE (private->impl)->set_child_shapes (window); + do_child_shapes (window, FALSE); } /** @@ -6254,13 +6316,9 @@ gdk_window_set_child_shapes (GdkWindow *window) void gdk_window_merge_child_shapes (GdkWindow *window) { - GdkWindowObject *private; - g_return_if_fail (GDK_IS_WINDOW (window)); - private = (GdkWindowObject *) window; - - GDK_WINDOW_IMPL_GET_IFACE (private->impl)->merge_child_shapes (window); + do_child_shapes (window, TRUE); } @@ -6650,10 +6708,12 @@ static gboolean point_in_window (GdkWindowObject *window, double x, double y) { - /* TODO: Input Shape */ return x >= 0 && x < window->width && - y >= 0 && y < window->height; + y >= 0 && y < window->height && + (window->shape == NULL || + gdk_region_point_in (window->shape, + x, y)); } static void diff --git a/gdk/gdkwindow.h b/gdk/gdkwindow.h index 5c475785f0..bc08cbc51b 100644 --- a/gdk/gdkwindow.h +++ b/gdk/gdkwindow.h @@ -337,6 +337,8 @@ struct _GdkWindowObject GdkWindowPaint *implicit_paint; GList *outstanding_moves; + + GdkRegion *shape; cairo_surface_t *cairo_surface; }; diff --git a/gdk/x11/gdkwindow-x11.c b/gdk/x11/gdkwindow-x11.c index 2b00d9582f..85f81e5135 100644 --- a/gdk/x11/gdkwindow-x11.c +++ b/gdk/x11/gdkwindow-x11.c @@ -4757,48 +4757,92 @@ gdk_add_to_span (struct _gdk_span **s, return; } -GdkRegion * -_gdk_windowing_window_get_shape (GdkWindow *window) +static GdkRegion * +xwindow_get_shape (Display *xdisplay, + Window window) { GdkRegion *shape; + GdkRectangle *rl; + XRectangle *xrl; + gint rn, ord, i; shape = NULL; #if defined(HAVE_SHAPE_EXT) - if (!GDK_WINDOW_DESTROYED (window) && - gdk_display_supports_shapes (GDK_WINDOW_DISPLAY (window))) + xrl = XShapeGetRectangles (xdisplay, + window, + ShapeBounding, &rn, &ord); + + if (rn == 0) + return NULL; + + if (ord != YXBanded) { - GdkRectangle *rl; - XRectangle *xrl; - gint rn, ord, i; - - xrl = XShapeGetRectangles (GDK_WINDOW_XDISPLAY (window), - GDK_WINDOW_XID (window), - ShapeBounding, &rn, &ord); - - if (ord != YXBanded) - { - /* This really shouldn't happen with any xserver, as they - generally convert regions to YXBanded internally */ - g_warning ("non YXBanded shape masks not supported"); - XFree (rl); - return NULL; - } + /* This really shouldn't happen with any xserver, as they + generally convert regions to YXBanded internally */ + g_warning ("non YXBanded shape masks not supported"); + XFree (xrl); + return NULL; + } - rl = g_new (GdkRectangle, rn); - for (i = 0; i < rn; i++) - { - rl[i].x = xrl[i].x; - rl[i].y = xrl[i].y; - rl[i].width = xrl[i].width; - rl[i].height = xrl[i].height; - } - - shape = _gdk_region_new_from_yxbanded_rects (rl, rn); - g_free (rl); - XFree (rl); + rl = g_new (GdkRectangle, rn); + for (i = 0; i < rn; i++) + { + rl[i].x = xrl[i].x; + rl[i].y = xrl[i].y; + rl[i].width = xrl[i].width; + rl[i].height = xrl[i].height; } + XFree (xrl); + + shape = _gdk_region_new_from_yxbanded_rects (rl, rn); + g_free (rl); +#endif + + return shape; +} + + +GdkRegion * +_gdk_windowing_get_shape_for_mask (GdkBitmap *mask) +{ + GdkDisplay *display; + Window window; + GdkRegion *region; + + display = gdk_drawable_get_display (GDK_DRAWABLE (mask)); + + window = XCreateSimpleWindow (GDK_DISPLAY_XDISPLAY (display), + GDK_SCREEN_XROOTWIN (gdk_display_get_default_screen (display)), + -1, -1, 1, 1, 0, + 0, 0); + XShapeCombineMask (GDK_DISPLAY_XDISPLAY (display), + window, + ShapeBounding, + 0, 0, + GDK_PIXMAP_XID (mask), + ShapeSet); + + region = xwindow_get_shape (GDK_DISPLAY_XDISPLAY (display), + window); + + XDestroyWindow (GDK_DISPLAY_XDISPLAY (display), + window); + + return region; +} + +GdkRegion * +_gdk_windowing_window_get_shape (GdkWindow *window) +{ +#if defined(HAVE_SHAPE_EXT) + if (!GDK_WINDOW_DESTROYED (window) && + gdk_display_supports_shapes (GDK_WINDOW_DISPLAY (window))) + return xwindow_get_shape (GDK_WINDOW_XDISPLAY (window), + GDK_WINDOW_XID (window)); #endif + + return NULL; } static void -- 2.30.2